home *** CD-ROM | disk | FTP | other *** search
/ Aminet 24 / Aminet 24 (1998)(GTI - Schatztruhe)[!][Apr 1998].iso / Aminet / util / cdity / IFX.lha / IFX / AmigaE / play / sound.e < prev    next >
Text File  |  1997-12-03  |  15KB  |  649 lines

  1. OPT MODULE
  2. OPT EXPORT
  3.  
  4. ->
  5. -> sound.m - simplify playing sounds in E
  6. ->
  7.  
  8. -> Flag ID's
  9. EXPORT SET 
  10.     SNDF_NO_DATATYPES,        -> Attempt to use datatypes
  11.     SNDF_PLAY_ASYNC,        -> Not implemented
  12.     SNDF_DUMMY
  13.  
  14. MODULE 'devices/audio'
  15. MODULE 'exec/ports', 'exec/lists', 'exec/nodes', 'exec/io', 'exec/execbase', 'exec'
  16. MODULE 'exec/memory'
  17. MODULE 'datatypes/datatypes', 'datatypes/datatypesclass', 'datatypes'
  18. MODULE 'graphics', 'graphics/gfxbase'
  19. MODULE 'dos', 'dos/dos'
  20. MODULE 'utility/tagitem'
  21. MODULE 'datatypes/soundclass'
  22. MODULE 'amigalib/io'
  23. MODULE 'tools/async'
  24.  
  25.     -> The sound object <-
  26. EXPORT OBJECT sound
  27.     filename    :PTR TO CHAR    -> Path of file to play
  28.     type        :LONG
  29.     data        :PTR TO CHAR
  30.     length        :LONG
  31.     period        :LONG
  32.     volume        :INT
  33.     cycles        :INT
  34. ENDOBJECT
  35.  
  36.     -> Auxiliary Objects <-
  37. OBJECT channel
  38.     current    :PTR TO ioaudio
  39.     sound    :PTR TO sound
  40. ENDOBJECT
  41.  
  42. OBJECT chans
  43.     left_a    :PTR TO channel
  44.     left_b    :PTR TO channel
  45.     right_a    :PTR TO channel
  46.     right_b    :PTR TO channel
  47. ENDOBJECT
  48.  
  49. -> Global objects and variables <-
  50. EXPORT DEF clock
  51. EXPORT DEF channels:PTR TO chans
  52.  
  53. -> Sound methods <-
  54.  
  55. EXPORT PROC load(fname:PTR TO CHAR, flags=NIL) OF sound HANDLE
  56.     DEF d,l,p,v,c                -> We'll use these to dodge E's annoying incapabilities
  57.     DEF obj=NIL                    -> Datatypes stuff
  58.     DEF file=NIL, 
  59.         data=NIL:PTR TO INT        -> RAW/8SVX stuff
  60.     DEF n
  61.     
  62.     IF fname
  63.         ->
  64.         -> Check if the file exists
  65.         ->
  66.         
  67.         IF FileLength(fname)=-1 THEN Throw("FILE", "BAD")
  68.         
  69.         ->
  70.         -> Load it
  71.         ->
  72.         
  73.         IF (flags AND SNDF_NO_DATATYPES)
  74.             IF datatypesbase THEN CloseLibrary(datatypesbase)
  75.             datatypesbase := NIL
  76.         ELSE
  77.             datatypesbase := OpenLibrary('datatypes.library', 39)
  78.         ENDIF
  79.         IF datatypesbase
  80.             self.type := "DTYP"
  81.             -> We can use datatypes to import the data,
  82.             -> but we'll play it ourself, to allow more
  83.             -> flexibility.
  84.             obj := NewDTObjectA(fname,
  85.                 [DTA_SOURCETYPE,DTST_FILE,
  86.                 DTA_GROUPID,     GID_SOUND,    -> Only load a sound file
  87.                 SDTA_VOLUME,     64,
  88.                 SDTA_CYCLES,    1,            -> Play only once
  89.                 NIL,         NIL])
  90.                 
  91.             -> Error checking
  92.             IF obj=NIL THEN Throw("DTYP", "OBJ")
  93.             
  94.             -> Read the attributes
  95.             IF GetDTAttrsA(obj,
  96.                 [SDTA_SAMPLE,        {d},
  97.                 SDTA_SAMPLELENGTH,    {l},
  98.                 SDTA_PERIOD,        {p},
  99.                 SDTA_VOLUME,        {v},
  100.                 SDTA_CYCLES,        {c},
  101.                 TAG_END])<5 THEN Throw("DTYP", "READ")
  102.             
  103.             -> Crash prevention
  104.             IF l=NIL THEN Throw("FILE", "LEN")
  105.             IF d=NIL THEN Throw("FILE", "LOAD")
  106.             
  107.             -> Copy the sample data
  108.             self.data := NewM(l, MEMF_CHIP)
  109.             CopyMem(d, self.data, l)
  110.             
  111.             -> Copy the other attributes
  112.             self.length := l
  113.             self.period := p
  114.             self.volume := v
  115.             self.cycles := c
  116.             
  117.             -> Now that we have all the stuff, we can just trash 
  118.             -> the whole datatypes thing.
  119.             DisposeDTObject(obj)
  120.             obj := NIL
  121.             CloseLibrary(datatypesbase)
  122.             datatypesbase := NIL
  123.         ELSE
  124.             -> Without the datatypes library, we are left to
  125.             -> using RAW or IFF (8svx) sounds, and figuring out
  126.             -> which ourselves.
  127.             
  128.             -> Open the file, first
  129.             file := as_Open(fname, MODE_OLDFILE)
  130.             
  131.             -> Check if it's an IFF
  132.             as_Read(file, {data}, 4)      -> Read the first four bytes
  133.             IF data = "FORM"
  134.                 -> Import it as an IFF
  135.                 
  136.                 -> The next LONG is the length of the sample.
  137.                 Read(file, {l}, 4)        -> Read it
  138.                 self.length := l    -> Save it
  139.                 
  140.                 -> Then we get the type
  141.                 Read(file, {data}, 4)    -> Read it
  142.                 
  143.                 IF data="8SVX"
  144.                     self.type := "8SVX"
  145.                     -> Now we need to go to the "VHDR" chunk
  146.                     REPEAT
  147.                         IF Read(file, {data}, 4) = -1 THEN Throw("FILE", "READ")
  148.                     UNTIL data="VHDR"
  149.                     
  150.                     -> Skip until the record rate
  151.                     Seek(file, 4, OFFSET_CURRENT)
  152.                     Read(file, {data}, 4)    -> Normal samples
  153.                     Read(file, {l}, 4)        -> Hi samples
  154.                     self.length := (l+data)
  155.                     Seek(file, 4, OFFSET_CURRENT) -> Some crap
  156.                     
  157.                         -> Get the play rate, set the period
  158.                     Read(file, data, 2)    -> Reading a WORD (so +2)
  159.                     self.period := rate2period(data[])
  160.                     
  161.                         -> Go the data section
  162.                     Seek(file, 0, OFFSET_BEGINNING)
  163.                     REPEAT
  164.                         -> Read with error checking
  165.                         IF Read(file, {data}, 4) = -1 THEN Throw("FILE", "READ")
  166.                     UNTIL data="BODY"
  167.                     -> Skip next four bytes for fun (SIZE OF HUNK, I guess...)
  168.                     Read(file, {data}, 4)
  169.                     
  170.                     -> Allocate the data field and read the hunk
  171.                     self.data := NewM(self.length, (MEMF_CHIP OR MEMF_CLEAR))
  172.                     
  173.                         -> Save the size of the block that was ACTUALLY read,
  174.                         -> in case the number we got before was crap.
  175.                         -> Read it, too.
  176.                     self.length := Read(file, self.data, self.length)
  177.                     IF self.length=-1 THEN Throw("FILE", "READ")
  178.                     
  179.                     -> Set some defaults
  180.                     self.volume := 64
  181.                     self.cycles := 1
  182.                 ELSE
  183.                     Throw("FILE", "FORM")
  184.                 ENDIF
  185.             ELSE
  186.                 self.type := "RAW"
  187.                 -> It's a RAW sample.
  188.                 
  189.                     -> Go to the end
  190.                 Seek(file, 0, OFFSET_END)    
  191.                 
  192.                     -> Return the beginning and save the filesize
  193.                 self.length := Seek(file, 0, OFFSET_BEGINNING)
  194.                 IF self.length=-1 THEN Throw("LOAD", "DOS")
  195.                 
  196.                     -> Allocate storage and read the data
  197.                 self.data    := NewM(self.length, MEMF_CHIP)
  198.                 self.length := Read(file, self.data, self.length)
  199.                     
  200.                     -> Insert other data as defaults
  201.                 self.cycles := 1
  202.                 self.period := rate2period(10000)
  203.                 self.volume := 64
  204.             ENDIF
  205.             Close(file)
  206.         ENDIF
  207.     ELSE
  208.         -> Check flags for if they want us to pop up a requester if
  209.         -> no name is given.
  210.         
  211.         -> I'll insert this later, because it's useless.
  212.     ENDIF
  213. EXCEPT
  214.     -> De-allocate everything.
  215.     IF obj                THEN DisposeDTObject(obj)
  216.     IF datatypesbase    THEN CloseLibrary(datatypesbase)
  217.     IF self.data         THEN END self.data
  218.     IF file                THEN Close(file)
  219.     self.length := NIL
  220.     
  221.     RETURN exception, exceptioninfo
  222. ENDPROC NIL, NIL
  223.  
  224. ->
  225. -> NAME
  226. ->    play -- Play sound
  227. ->
  228. -> SYNOPSIS
  229. ->    sound.play(channel, wait=0)
  230. ->
  231. -> FUNCTION
  232. ->    This will play the sound that has previously been loaded or
  233. -> put into the object.  It does not currently check to make sure
  234. -> the data is OK, but if nothing's there, make sure length=0 and
  235. -> nothing will go wrong.
  236. ->
  237. -> INPUTS
  238. ->    channel -- channel to play in (0-3)
  239. ->    wait    -- If 1 (default), then wait for the sound to complete
  240. ->             before returning.  If zero, then begin io and return
  241. ->             immediately.
  242. ->
  243. -> RESULTS
  244. ->    error, id -- Error code and an additional id.  This will be one of:
  245. ->               "MEM" , "OUT"  -- Out of memory
  246. ->               "INIT", "PORT" -- Failed to create message port
  247. ->               "INIT", "REQ"  -- Failed to create IORequest structure
  248. ->               "ADIO", "OPEN" -- Failed to open audio device
  249. ->               "ADIO", "ALOC" -- Failed to allocate channels
  250. ->               These values are copied striaght from the function's
  251. ->               exception and exceptioninfo return.
  252. ->
  253.  
  254. EXPORT PROC play(channel, wait=0) OF sound HANDLE
  255.     DEF chan=NIL:PTR TO channel
  256.     DEF port=NIL:PTR TO mp
  257.     DEF req=NIL:PTR TO ioaudio
  258.     DEF error
  259.     
  260.     -> Pick our channel
  261.     IF channels=NIL 
  262.         NEW channels
  263.         IF channels=NIL THEN Throw("MEM", "OUT")
  264.     ENDIF
  265.     chan := channels.num(channel)
  266.     
  267.     -> Stop any sound that's playing.  If they want us to wait,
  268.     -> then wait first.
  269.     chan.stop(wait)    
  270.     
  271.     -> Create a replyport
  272.     port := CreateMsgPort()
  273.     IF port=NIL THEN Throw("INIT", "PORT")
  274.     
  275.     -> Create the request
  276.     req := CreateIORequest(port, SIZEOF ioaudio)
  277.     IF req=NIL THEN Throw("INIT", "REQ")
  278.     
  279.     -> Open the device
  280.     req.io.command    := ADCMD_ALLOCATE
  281.     req.io.mn.ln.pri:= -60
  282.     req.data        := [Shl(1, channel)]:CHAR
  283.     req.length         := 1
  284.     IF (error := OpenDevice('audio.device', 0, req, 0))
  285.         SELECT error
  286.             CASE ADIOERR_ALLOCFAILED
  287.                 Throw("ADIO", "ALOC")
  288.             DEFAULT
  289.                 Throw("ADIO", "OPEN")
  290.         ENDSELECT
  291.     ENDIF
  292.     
  293.     -> Initialize the request for playing
  294.     req.io.command    := CMD_WRITE
  295.     req.io.flags    := ADIOF_PERVOL
  296.     req.volume         := self.volume
  297.     req.period        := self.period
  298.     req.cycles        := self.cycles
  299.     req.data        := self.data
  300.     req.length        := (IF self.length <= 131072 THEN self.length ELSE 131072)
  301.     
  302.     -> Play the sound
  303.     beginio(req)
  304.     
  305.     -> Now we'll store the request in the channel.
  306.     chan.current     := req
  307.     chan.sound        := self    
  308.     
  309.     -> Wait, if requested
  310.     IF wait THEN self.wait(channel)
  311. EXCEPT    
  312.     IF port THEN DeleteMsgPort(port)
  313.     IF req  THEN DeleteIORequest(req)
  314.     RETURN exception, exceptioninfo
  315. ENDPROC NIL, NIL
  316.  
  317. ->
  318. -> N